package cn.bonoon.services;

import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.bonoon.core.IRoleEditor;
import cn.bonoon.core.plugins.RoleService;
import cn.bonoon.entities.plugins.AccountEntity;
import cn.bonoon.entities.plugins.AccountPrivilegeEntity;
import cn.bonoon.entities.plugins.PrivilegeEntity;
import cn.bonoon.entities.plugins.RoleAssignEntity;
import cn.bonoon.entities.plugins.RoleEntity;
import cn.bonoon.kernel.events.OperateEvent;
import cn.bonoon.kernel.menus.IModuleInfo;
import cn.bonoon.kernel.menus.ItemInfo;
import cn.bonoon.kernel.menus.ModuleManager;
import cn.bonoon.kernel.menus.TabInfo;
import cn.bonoon.kernel.menus.TabItem;
import cn.bonoon.kernel.support.IOperator;
import cn.bonoon.kernel.support.services.AbstractService;

@Service
@Transactional(readOnly = true)
public class RoleServiceImpl extends AbstractService<RoleEntity> implements RoleService {
	
	private final String _0 = "select x from PrivilegeEntity x where x.role.id=?";
	private final String _1 = "select x from RoleEntity x where x.status=1 and x.deleted=false";
	private final String _1_1 = "select x.role from RoleAssignEntity x where  x.account.id=? and x.role.deleted=false";
	private final String _2 = "select x from RoleEntity x where x.status=1 and x.deleted=false and x.ownerId=?";
	private final String _3 = "select x.role.id from RoleAssignEntity x where x.account.id=?";
	private final String _4 = "select x from RoleAssignEntity x where x.account.id=?";
	private final String _5 = "select x.meunId from PrivilegeEntity x where x.role.id=?";
	private final String _6 = "select x.actions from PrivilegeEntity x where x.meunId=? and x.role.id=?";
	private final String _7 = "select x from AccountPrivilegeEntity x where x.account.id=? and x.moduleId=? and x.channelId=?";

	@Autowired
	private ModuleManager moduleManager;

	@Override
	public List<RoleEntity> getRoles(IOperator opt) {
		if(opt.isSuper()){
			return __list(RoleEntity.class, _1);
		}
		return __list(RoleEntity.class, _2, opt.getOwnerId());
	}
	
	@Override
	public List<Long> getRolesId(Long id) {
		return __list(Long.class, _3, id);
	}
	
	private List<RoleAssignEntity> _getRoleAssign(Long id) {
		return __list(RoleAssignEntity.class, _4, id);
	}
	
	@Override
	@Transactional
	public void saveAssignRole(IOperator opt, Long aid, Long... roleIds) {
		List<RoleAssignEntity> roleAssign = _getRoleAssign(aid);
		if (null != roleIds && roleIds.length > 0) {
			RoleAssignEntity rae;
			Date now = new Date();
			for (Long id : roleIds) {
				if (roleAssign.isEmpty()) {
					rae = new RoleAssignEntity();
					rae.setAccount(entityManager.find(AccountEntity.class, aid));
					rae.setCreateAt(now);
					rae.setCreatorId(opt.getId());
					rae.setRole(__get(id));
					entityManager.persist(rae);
				} else {
					rae = roleAssign.remove(0);
					rae.setRole(__get(id));
					entityManager.merge(rae);
				}
			}
		}
		for (RoleAssignEntity entity : roleAssign) {
			entityManager.remove(entity);
		}
	}
	
	@Override
	protected RoleEntity __update(OperateEvent event, RoleEntity entity) {
		IModuleInfo module = moduleManager.module(entity.getModuleKey());
		entity.setModuleName(module.getName());
		RoleEntity re = super.__update(event, entity);
		List<PrivilegeEntity> pre = __list(PrivilegeEntity.class, _0, re.getId());
		IRoleEditor roleEditor = (IRoleEditor) event.getSource();
		if (null != roleEditor.getPrivileges()) {
			for (String plg : roleEditor.getPrivileges()) {
				if (pre.isEmpty()) {
					readPrivilege(re, plg, event);
				} else {
					PrivilegeEntity pe = pre.remove(0);
					if(readPrivilege(pe, plg, event)){
						entityManager.merge(pe);
					}else{
						pre.add(pe);
					}
				}
			}
		}
		for (PrivilegeEntity p : pre) {
			entityManager.remove(p);
		}
		return re;
	}

	@Override
	protected RoleEntity __save(OperateEvent event, RoleEntity entity) {
		IModuleInfo module = moduleManager.module(entity.getModuleKey());
		entity.setModuleName(module.getName());
		RoleEntity re = super.__save(event, entity);
		IRoleEditor roleEditor = (IRoleEditor) event.getSource();
		if (null != roleEditor.getPrivileges()) {
			for (String plg : roleEditor.getPrivileges()) {
				readPrivilege(re, plg, event);
			}
		}
		return re;
	}

	private void readPrivilege(RoleEntity re, String name, OperateEvent event) {
		PrivilegeEntity pe = new PrivilegeEntity();
		if(readPrivilege(pe, name, event)){
			pe.setRole(re);
			entityManager.persist(pe);
		}
	}

	private boolean readPrivilege(PrivilegeEntity pe, String name, OperateEvent event) {
		ItemInfo menu = moduleManager.item(name);
		pe.setMeunId(name);
		if(null != menu){
			pe.setChannelId(menu.getChannel().getId());
			pe.setModuleId(menu.getModule().getId());
			long actions = 0;
			String[] values = event.getStringArray(name + "_actions");
			if (null != values) {
				for (String val : values) {
					actions |= 1 << Long.parseLong(val);
				}
			}
			pe.setActions(actions);
			return true;
		}
		
		TabItem tabItem = moduleManager.tabItem(name);
		if(null != tabItem && tabItem.getTabs() instanceof TabInfo){
			TabInfo ti = (TabInfo)tabItem.getTabs();
			pe.setChannelId(ti.getChannel().getId());
			pe.setModuleId(ti.getModule().getId());
			return true;
		}
		
		return false;
	}

	@Override
	public List<String> getPrivilegeMeunId(Long roleId) {
		return __list(String.class, _5, roleId);
	}

	@Override
	public long getActions(String meunId, Long roleId) {
		if(null != roleId && roleId > 0){
			Long as = __first(Long.class, _6, meunId, roleId);
			if(null != as) return as;
		}
		return 0L;
	}

	@Override
	@Transactional
	public void saveAdminPrivilege(IOperator opt, Long aid, String moduleId, String channelId, String... menuIds) {
		List<AccountPrivilegeEntity> aps = __list(AccountPrivilegeEntity.class, _7, aid, moduleId, channelId);
		if (null != menuIds && menuIds.length > 0) {
			for (String menuId : menuIds) {
				if (aps.isEmpty()) {
					AccountPrivilegeEntity entity = new AccountPrivilegeEntity();
					entity.setAccount(entityManager.find(AccountEntity.class, aid));
					entity.setChannelId(channelId);
					entity.setMeunId(menuId);
					entity.setModuleId(moduleId);
					entityManager.persist(entity);
				} else {
					AccountPrivilegeEntity entity = aps.remove(0);
					entity.setChannelId(channelId);
					entity.setMeunId(menuId);
					entity.setModuleId(moduleId);
					entityManager.merge(entity);
				}
			}
		}
		
		for (AccountPrivilegeEntity entity : aps) {
			entityManager.remove(entity);
		}
	}

	@Override
	public List<RoleEntity> getRoles(Long accountId) {
		return __list(RoleEntity.class, _1_1, accountId);
	}

	@Override
	public List<AccountPrivilegeEntity> getAccountPrivilege(Long accountId, String moduleId, String channelId) {
		return __list(AccountPrivilegeEntity.class, _7, accountId, moduleId, channelId);
	}
}
